from flask import Flask, request, send_from_directory, jsonify, send_file
import os
import uuid
import sqlite3
from contextlib import contextmanager
import shutil
import zipfile
from io import BytesIO
import pandas as pd
import json  # 添加这行代码来导入json模块


app = Flask(__name__)

# 配置上传和下载目录
BASE_DIR = os.path.join(os.getcwd())
DOWNLOAD_DIR = os.path.join(BASE_DIR, 'downloads')
UPLOAD_DIR = os.path.join(BASE_DIR, 'uploads')
EXCEL_DIR = os.path.join(BASE_DIR, 'excel')

# 定义设备列表
DEVICES = ['SW610_HB02', 'SW610_H01', 'SW610_HD01', 'SW612_HD01', 'SW778_A01']

# 设置JSON编码为UTF-8，避免中文显示为Unicode
app.config['JSON_AS_ASCII'] = False
app.json.ensure_ascii = False

# 设置最大文件大小为1MB
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024

# 确保目录存在
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
os.makedirs(UPLOAD_DIR, exist_ok=True)
os.makedirs(EXCEL_DIR, exist_ok=True)

# 为每个设备创建独立目录
for device in DEVICES:
    os.makedirs(os.path.join(DOWNLOAD_DIR, device), exist_ok=True)
    os.makedirs(os.path.join(EXCEL_DIR, device), exist_ok=True)

# 允许上传的文件类型
ALLOWED_EXTENSIONS = {'json'}

def allowed_file(filename):
    """检查文件扩展名是否允许"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

# 设置数据库路径
DB_PATH = os.path.join(os.getcwd(), 'data.db')

# 初始化数据库
def init_db():
    """初始化数据库表"""
    with get_db_connection() as conn:
        conn.execute('''CREATE TABLE IF NOT EXISTS json_data
                     (id INTEGER PRIMARY KEY AUTOINCREMENT,
                      name TEXT NOT NULL,
                      count INTEGER NOT NULL,
                      imei TEXT,
                      device_type TEXT,  -- 添加项目类型字段
                      created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
        
        # 添加上传记录表
        conn.execute('''CREATE TABLE IF NOT EXISTS upload_records
                        (id INTEGER PRIMARY KEY AUTOINCREMENT,
                         folder_name TEXT NOT NULL,
                         upload_type TEXT NOT NULL,  -- 'test_case' 或 'excel'
                         client_ip TEXT,
                         user_agent TEXT,
                         device_name TEXT,  -- 添加设备名称字段
                         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)''')
        conn.commit()

@contextmanager
def get_db_connection():
    """获取数据库连接的上下文管理器"""
    conn = sqlite3.connect(DB_PATH)
    try:
        yield conn
    finally:
        conn.close()

@app.route('/')
def index():
    """主页显示简单说明"""
    # 从数据库获取数据列表
    data_list = []
    try:
        with get_db_connection() as conn:
            cursor = conn.execute('SELECT id, name, count, imei, created_at, device_type FROM json_data ORDER BY created_at DESC')
            data_list = cursor.fetchall()
    except Exception as e:
        print(f"数据库查询错误: {e}")
    
    # 从数据库获取上传记录
    upload_records = []
    try:
        with get_db_connection() as conn:
            cursor = conn.execute('''SELECT folder_name, upload_type, client_ip, user_agent, device_name, created_at 
                                   FROM upload_records ORDER BY created_at DESC LIMIT 10''')
            upload_records = cursor.fetchall()
    except Exception as e:
        print(f"上传记录查询错误: {e}")
    
    # 构建上传记录的HTML
    upload_html = ""
    if upload_records:
        upload_html += """
        <h2>最近上传记录</h2>
        <div class="data-container">
        <table>
            <thead>
                <tr>
                    <th>用例名称</th>
                    <th>上传类型</th>
                    <th>客户端IP</th>
                    <th>用户代理</th>
                    <th>设备名称</th>
                    <th>上传时间</th>
                </tr>
            </thead>
            <tbody>
        """
        for row in upload_records:
            # 将UTC时间转换为本地时间(+8时区)
            from datetime import datetime, timezone, timedelta
            utc_time = datetime.strptime(row[5], '%Y-%m-%d %H:%M:%S')
            local_time = utc_time.replace(tzinfo=timezone.utc).astimezone(timezone(timedelta(hours=8)))
            formatted_time = local_time.strftime('%Y-%m-%d %H:%M:%S')
            
            upload_type = "测试用例" if row[1] == "test_case" else "Excel测试用例"
            # 修改用户代理信息显示，完整显示而不截断
            user_agent_display = row[3] if row[3] else "未知"
            device_name_display = row[4] if row[4] else "未知"
            upload_html += f"""
                <tr>
                    <td>{row[0]}</td>
                    <td>{upload_type}</td>
                    <td>{row[2] if row[2] else "未知"}</td>
                    <td>{user_agent_display}</td>
                    <td>{device_name_display}</td>
                    <td>{formatted_time}</td>
                </tr>
            """
        upload_html += """
            </tbody>
        </table>
        </div>
        """
    else:
        upload_html = '<div class="no-data"><p>暂无上传记录</p></div>'
    
    # 构建数据列表的HTML
    data_html = ""
    if data_list:
        data_html += """
        <h2>测试报告记录</h2>
        <style>
            table {
                border-collapse: collapse;
                width: 100%;
                margin: 20px 0;
            }
            th, td {
                border: 1px solid #ddd;
                padding: 12px;
                text-align: left;
            }
            th {
                background-color: #f2f2f2;
                font-weight: bold;
            }
            tr:nth-child(even) {
                background-color: #f9f9f9;
            }
            tr:hover {
                background-color: #f5f5f5;
            }
            .data-container {
                margin: 20px 0;
                box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                border-radius: 5px;
                overflow: hidden;
            }
            .no-data {
                text-align: center;
                padding: 20px;
                color: #666;
            }
            .delete-btn {
                background-color: #dc3545;
                color: white;
                border: none;
                padding: 5px 10px;
                cursor: pointer;
                border-radius: 3px;
            }
            .delete-btn:hover {
                background-color: #c82333;
            }
        </style>
        <div class="data-container">
        <table>
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用例名称</th>
                    <th>运行次数</th>
                    <th>IMEI</th>
                    <th>项目类型</th>
                    <th>报告时间</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
        """
        for row in data_list:
            # 将UTC时间转换为本地时间(+8时区)
            from datetime import datetime, timezone, timedelta
            utc_time = datetime.strptime(row[4], '%Y-%m-%d %H:%M:%S')
            local_time = utc_time.replace(tzinfo=timezone.utc).astimezone(timezone(timedelta(hours=8)))
            formatted_time = local_time.strftime('%Y-%m-%d %H:%M:%S')
            
            imei_display = row[3] if row[3] else "N/A"
            device_type_display = row[5] if row[5] else "未指定"
            data_html += f"""
                <tr>
                    <td>{row[0]}</td>
                    <td>{row[1]}</td>
                    <td>{row[2]}</td>
                    <td>{imei_display}</td>
                    <td>{device_type_display}</td>
                    <td>{formatted_time}</td>
                    <td><button class="delete-btn" onclick="deleteRecord({row[0]})">删除</button></td>
                </tr>
            """
        data_html += """
            </tbody>
        </table>
        </div>
        """
    else:
        data_html = '<div class="no-data"><p>暂无测试报告记录</p></div>'
    
    return f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>RTOS自动化测试服务器</title>
        <meta charset="UTF-8">
        <style>
            body {{
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                max-width: 1200px;
                margin: 0 auto;
                padding: 20px;
                background-color: #f8f9fa;
            }}
            h1, h2 {{
                color: #333;
            }}
            h1 {{
                border-bottom: 2px solid #007bff;
                padding-bottom: 10px;
            }}
            p {{
                line-height: 1.6;
            }}
            a {{
                color: #007bff;
                text-decoration: none;
            }}
            a:hover {{
                text-decoration: underline;
            }}
            .section {{
                background: white;
                padding: 20px;
                margin: 20px 0;
                border-radius: 5px;
                box-shadow: 0 2px 4px rgba(0,0,0,0.1);
            }}
            .footer {{
                text-align: center;
                margin-top: 40px;
                padding: 20px;
                border-top: 1px solid #eee;
                color: #666;
                font-size: 0.9em;
            }}
            .device-selector {{
                margin: 10px 0;
                padding: 10px;
                background-color: #e9ecef;
                border-radius: 5px;
            }}
            .device-selector label {{
                font-weight: bold;
                margin-right: 10px;
            }}
            .device-selector select {{
                padding: 5px;
                border-radius: 3px;
                border: 1px solid #ccc;
            }}
        </style>
    </head>
    <body>
        <h1>RTOS自动化测试服务器</h1>
        
        <div class="device-selector">
            <label for="deviceSelect">选择设备:</label>
            <select id="deviceSelect" onchange="changeDevice()">
                <option value="SW610_HB02">设备 SW610_HB02</option>
                <option value="SW610_H01">设备 SW610_H01</option>
                <option value="SW610_HD01">设备 SW610_HD01</option>
                <option value="SW612_HD01">设备 SW612_HD01</option>
                <option value="SW778_A01">设备 SW778_A01</option>
            </select>
        </div>
        
        <div class="section">
            <p>支持文件上传和下载功能</p>
            <p>访问 <a href="/downloads" id="downloadsLink">/downloads</a> 查看测试用例</p>
            <p>访问 <a href="/excel" id="excelLink">/excel</a> 查看Excel测试用例</p>
            <p>使用 POST 请求上传文件到 /upload</p>
            <p>使用 POST 请求发送 JSON 数据到 /api/data</p>
        </div>
        
        <div class="section">
            <h2>测试用例目录上传（研发自用）</h2>
            <form id="folderUploadForm">
                <input type="file" id="folderInput" webkitdirectory directory multiple>
                <input type="password" id="uploadPassword" placeholder="请输入上传密码">
                <button type="button" onclick="uploadFolder()">上传</button>
            </form>
            
            <div id="uploadStatus"></div>
        </div>
        
        <div class="section">
            <h2>Excel测试用例目录</h2>
            <p style="color: red; font-weight: bold;">注意：1.测试用例目录中的文件，excel表格名字需要在前面增加"10_","11_"等开头，依次往上累加，代表表格执行顺序。2.编辑中的excel表需要关闭再上传</p>
            <p><a href="/excel" id="excelTestLink">查看Excel测试用例目录</a></p>
            <form id="excelFolderUploadForm">
                <input type="file" id="excelFolderInput" webkitdirectory directory multiple>
                <button type="button" onclick="uploadExcelFolder()">上传Excel测试用例</button>
            </form>
            <div id="excelUploadStatus"></div>
        </div>
        
        <!-- 调整模块显示顺序，将上传记录放在测试报告记录之前 -->
        <div class="section">
            {upload_html}
        </div>
        
        <div class="section">
            {data_html}
        </div>
        
        <div class="footer">
            <p>甄十信息科技RTOS开发团队：何雄武、自动化策划：叶力军</p>
        </div>
        
        <script>
        // 获取当前选择的设备
        function getCurrentDevice() {{
            return document.getElementById('deviceSelect').value;
        }}
        
        // 设备切换时更新链接并保存到本地存储
        function changeDevice() {{
            const device = getCurrentDevice();
            document.getElementById('downloadsLink').href = '/downloads?device=' + device;
            document.getElementById('excelLink').href = '/excel?device=' + device;
            document.getElementById('excelTestLink').href = '/excel?device=' + device;
            
            // 保存设备选择到本地存储
            localStorage.setItem('selectedDevice', device);
        }}
        
        // 页面加载时初始化设备相关链接和设备选择
        window.onload = function() {{
            // 从本地存储中获取之前选择的设备
            const savedDevice = localStorage.getItem('selectedDevice');
            if (savedDevice) {{
                document.getElementById('deviceSelect').value = savedDevice;
            }}
            changeDevice();
        }};
        
        // 通过POST请求获取子目录信息
        function getSubdirectories(subdirPath = '') {{
            const device = getCurrentDevice();
            
            // 构造请求体
            const requestData = {{
                device: device
            }};
            
            // 发送POST请求到/api/subdirectories接口
            return fetch('/api/subdirectories' + (subdirPath ? '/' + subdirPath : ''), {{
                method: 'POST',
                headers: {{
                    'Content-Type': 'application/json'
                }},
                body: JSON.stringify(requestData)
            }})
            .then(response => response.json())
            .catch(error => {{
                console.error('获取子目录信息失败:', error);
                throw error;
            }});
        }}
        
        function uploadFolder() {{
            const folderInput = document.getElementById('folderInput');
            const files = folderInput.files;
            
            if (files.length === 0) {{
                alert('请选择一个文件夹');
                return;
            }}
            
            // 获取上传密码
            const password = document.getElementById('uploadPassword').value;
            if (!password) {{
                alert('请输入上传密码');
                return;
            }}
            
            // 获取文件夹名称（所有文件的共同父目录）
            let folderName = '';
            if (files[0].webkitRelativePath) {{
                folderName = files[0].webkitRelativePath.split('/')[0];
            }} else {{
                folderName = 'uploaded_folder';
            }}
            
            const statusDiv = document.getElementById('uploadStatus');
            statusDiv.innerHTML = '<p>正在上传文件夹: ' + folderName + '</p>';
            
            // 创建一个FormData对象来存储所有文件
            const formData = new FormData();
            
            // 添加密码到FormData
            formData.append('password', password);
            
            // 将所有文件添加到FormData中
            for (let i = 0; i < files.length; i++) {{
                formData.append('files', files[i]);
            }}
            
            // 获取当前选择的设备
            const device = getCurrentDevice();
            
            // 发送POST请求到服务器
            fetch('/upload-folder', {{
                method: 'POST',
                body: formData,
                headers: {{
                    'X-Device-Name': device
                }}
            }})
            .then(response => response.json())
            .then(data => {{
                if (data.success) {{
                    statusDiv.innerHTML = '<p style="color: green;">文件夹上传成功！</p>';
                    // 上传成功后延迟2秒再刷新页面，让用户能看到成功提示
                    setTimeout(() => {{
                        location.reload();
                    }}, 2000);
                }} else {{
                    statusDiv.innerHTML = '<p style="color: red;">上传失败: ' + data.error + '</p>';
                }}
            }})
            .catch(error => {{
                console.error('Upload error:', error);
                statusDiv.innerHTML = '<p style="color: red;">上传出错: ' + error.message + '</p>';
            }});
        }}
        
        function uploadExcelFolder() {{
            const folderInput = document.getElementById('excelFolderInput');
            const files = folderInput.files;
            
            if (files.length === 0) {{
                alert('请选择一个文件夹');
                return;
            }}
            
            // 获取文件夹名称（所有文件的共同父目录）
            let folderName = '';
            if (files[0].webkitRelativePath) {{
                folderName = files[0].webkitRelativePath.split('/')[0];
            }} else {{
                folderName = 'excel_report';
            }}
            
            const statusDiv = document.getElementById('excelUploadStatus');
            statusDiv.innerHTML = '<p>正在上传Excel测试用例文件夹: ' + folderName + '</p>';
            
            // 创建一个FormData对象来存储所有文件
            const formData = new FormData();
            
            // 将所有文件添加到FormData中
            for (let i = 0; i < files.length; i++) {{
                formData.append('files', files[i]);
            }}
            
            // 获取当前选择的设备
            const device = getCurrentDevice();
            
            // 发送POST请求到服务器
            fetch('/upload-excel-folder', {{
                method: 'POST',
                body: formData,
                headers: {{
                    'X-Device-Name': device
                }}
            }})
            .then(response => response.json())
            .then(data => {{
                if (data.success) {{
                    statusDiv.innerHTML = '<p style="color: green;">Excel测试用例文件夹上传成功！</p>';
                    // 上传成功后延迟2秒再刷新页面，让用户能看到成功提示
                    setTimeout(() => {{
                        location.reload();
                    }}, 2000);
                }} else {{
                    statusDiv.innerHTML = '<p style="color: red;">上传失败: ' + data.error + '</p>';
                }}
            }})
            .catch(error => {{
                console.error('Upload error:', error);
                statusDiv.innerHTML = '<p style="color: red;">上传出错: ' + error.message + '</p>';
            }});
        }}
        
        function deleteRecord(id) {{
            if (confirm('确定要删除这条记录吗？')) {{
                fetch('/api/data/' + id, {{
                    method: 'DELETE'
                }})
                .then(response => response.json())
                .then(data => {{
                    if (data.success) {{
                        alert('删除成功');
                        location.reload();
                    }} else {{
                        alert('删除失败: ' + data.error);
                    }}
                }})
                .catch(error => {{
                    alert('删除出错: ' + error.message);
                }});
            }}
        }}
        </script>
    </body>
    </html>
    """

@app.route('/downloads')
def list_downloads():
    """列出所有可下载的文件和子目录"""
    # 获取设备名称，如果未指定则默认为SW610H01
    device_name = request.args.get('device') or request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
    if device_name not in DEVICES:
        device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备SW610H01
    
    def build_directory_tree(base_path, relative_path=''):
        items = os.listdir(os.path.join(base_path, relative_path))
        files = []
        directories = []
        
        for item in items:
            item_path = os.path.join(relative_path, item)
            full_path = os.path.join(base_path, item_path)
            if os.path.isdir(full_path):
                directories.append(item_path)
            else:
                files.append(item_path)
        
        # 对文件和目录名进行排序
        files.sort()
        directories.sort()
        
        return directories, files
    
    def generate_html_tree(base_path, current_path=''):
        directories, files = build_directory_tree(base_path, current_path)
        
        html_parts = []
        
        # 显示当前目录下的文件（已排序）
        for file in files:
            file_name = os.path.basename(file)
            html_parts.append(f'<li>{file_name}</li>')
        
        # 显示当前目录下的子目录（已排序）
        for directory in directories:
            dir_name = os.path.basename(directory)
            dir_url = f"/downloads?device={device_name}&path={directory}"
            html_parts.append(f'<li><strong>{dir_name}/</strong>')
            # 递归显示子目录内容
            sub_html = generate_html_tree(base_path, directory)
            if sub_html:
                html_parts.append(f'<ul>{sub_html}</ul>')
            html_parts.append('</li>')
        
        return ''.join(html_parts)
    
    # 获取请求中的路径参数
    path = request.args.get('path', '')
    
    # URL解码路径参数，确保正确处理中文和特殊字符
    from urllib.parse import unquote
    decoded_path = unquote(path)
    
    # 构建完整路径，包含设备目录
    full_path = os.path.join(DOWNLOAD_DIR, device_name, decoded_path)
    
    # 检查路径是否在下载目录内（防止路径遍历攻击）
    if not os.path.abspath(full_path).startswith(os.path.abspath(os.path.join(DOWNLOAD_DIR, device_name))):
        return "无效路径", 400
    
    if not os.path.exists(full_path):
        return "路径不存在", 404
    
    if not os.path.isdir(full_path):
        return "路径不是目录", 400
    
    # 生成目录树HTML
    file_tree_html = generate_html_tree(os.path.join(DOWNLOAD_DIR, device_name), decoded_path)
    
    if not file_tree_html:
        file_tree_html = "目录为空"
    
    return f"""
    <h1>可下载文件 (设备: {device_name})</h1>
    <p>当前路径: /{decoded_path}</p>
    <p><a href="/">返回主页</a></p>
    <ul>
        {file_tree_html}
    </ul>
    <p><a href="/">返回主页</a></p>
    """

@app.route('/downloads/<device_name>/<path:filename>')
@app.route('/downloads/<path:filename>')
def download_file(filename, device_name="SW610_HB02"):
    """下载指定文件"""
    try:
        # 检查device_name是否是有效的设备名称
        if device_name not in DEVICES:
            # 如果device_name不是有效设备，则认为它是filename的一部分
            # 重新安排参数
            filename = os.path.join(device_name, filename) if filename else device_name
            # 获取设备名称，如果未指定则默认为A
            device_name = request.args.get('device') or request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
            if device_name not in DEVICES:
                device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备SW610H01
        
        # URL解码filename，确保正确处理中文和特殊字符
        from urllib.parse import unquote
        filename = unquote(filename)
        
        # 构建完整路径，包含设备目录
        full_path = os.path.join(DOWNLOAD_DIR, device_name, filename)
        
        print(f"Device: {device_name}, Filename: {filename}, Full path: {full_path}")  # 调试信息
        
        # 检查路径是否在设备下载目录内（防止路径遍历攻击）
        if not os.path.abspath(full_path).startswith(os.path.abspath(os.path.join(DOWNLOAD_DIR, device_name))):
            return "无效路径", 400
        
        if not os.path.exists(full_path):
            return "文件不存在", 404
        
        if os.path.isdir(full_path):
            return "路径是目录，不是文件", 400
        
        # 提取目录和文件名
        directory = os.path.dirname(full_path)
        file_name = os.path.basename(full_path)
        
        return send_from_directory(directory, file_name, as_attachment=True)
    except Exception as e:
        return f"下载文件时出错: {str(e)}", 500

@app.route('/upload', methods=['POST'])
def upload_file():
    """上传文件"""
    # 检查是否有文件上传
    if 'file' not in request.files:
        return jsonify({"error": "没有文件部分"}), 400
    
    file = request.files['file']
    
    # 检查文件名
    if file.filename == '':
        return jsonify({"error": "没有选择文件"}), 400
    
    # 检查文件大小
    if file.content_length and file.content_length > app.config['MAX_CONTENT_LENGTH']:
        return jsonify({"error": "文件大小超过1MB限制"}), 400
    
    # 检查文件类型
    if file and allowed_file(file.filename):
        # 生成唯一文件名，避免冲突
        filename = str(uuid.uuid4()) + '.' + file.filename.rsplit('.', 1)[1].lower()
        file.save(os.path.join(UPLOAD_DIR, filename))
        return jsonify({"success": True, "filename": filename}), 201
    else:
        return jsonify({"error": "不允许的文件类型"}), 400

@app.route('/api/data', methods=['POST'])
def process_data():
    """处理POST请求的数据"""
    # 使用更宽松的JSON解析方式
    data = request.get_json(force=True, silent=True)
    
    # 如果通过标准方式无法获取数据，尝试从原始数据中解析
    if not data:
        try:
            # 获取原始数据并尝试手动解析
            raw_data = request.get_data()
            if raw_data:
                import json
                # 尝试解码原始数据
                # 首先尝试UTF-8解码，如果失败则尝试其他编码
                try:
                    decoded_data = raw_data.decode('utf-8')
                except UnicodeDecodeError:
                    decoded_data = raw_data.decode('utf-8', errors='ignore')
                
                # 清理数据：去除可能的BOM标记和首尾空白字符
                decoded_data = decoded_data.strip('\ufeff').strip()
                
                # 检查数据是否以引号开始（非标准JSON）
                if decoded_data.startswith("'") and not decoded_data.startswith("'{{"):
                    # 尝试去除首尾单引号
                    if decoded_data.endswith("'"):
                        decoded_data = decoded_data[1:-1]
                    # 修复转义的单引号
                    decoded_data = decoded_data.replace("\\'", "'")
                
                # 处理键值对之间的空格问题
                # 移除JSON键值对中可能存在的多余空格
                import re
                # 移除键和值周围的多余空格，但保留字符串值内部的空格
                decoded_data = re.sub(r'"\s*:\s*', '":', decoded_data)  # 处理标准JSON格式
                decoded_data = re.sub(r"'\s*:\s*", "':", decoded_data)  # 处理单引号格式
                
                data = json.loads(decoded_data)
        except Exception as e:
            print(f"手动解析JSON失败: {e}")
            pass
    
    # 添加更详细的调试信息
    if not data:
        # 记录请求信息用于调试
        content_type = request.headers.get('Content-Type', '')
        raw_data = request.get_data()
        data_preview = raw_data.decode('utf-8', errors='replace')[:200] if raw_data else "No data"
        print(f"JSON解析失败 - Content-Type: {content_type}")
        print(f"Raw data length: {len(raw_data) if raw_data else 0}")
        print(f"Data preview: {data_preview}")
        print(f"Raw bytes: {raw_data[:200] if raw_data else b'No data'}")
        
        # 尝试更多解析方法
        if raw_data:
            try:
                import json
                import re
                # 尝试使用正则表达式提取JSON部分
                raw_str = raw_data.decode('utf-8', errors='ignore')
                # 查找可能的JSON对象
                json_match = re.search(r'\{{.*\}}', raw_str, re.DOTALL)
                if json_match:
                    json_str = json_match.group(0)
                    # 处理键值对之间的空格
                    json_str = re.sub(r'"\s*:\s*', '":', json_str)
                    json_str = re.sub(r"'\s*:\s*", "':", json_str)
                    data = json.loads(json_str)
                    print(f"通过正则表达式提取的JSON: {json_str}")
            except Exception as regex_e:
                print(f"正则表达式解析也失败: {regex_e}")
        
        if not data:
            return jsonify({"error": "无效的JSON数据"}), 400
    
    # 检查必要字段
    if 'name' not in data or 'count' not in data:
        return jsonify({"error": "缺少必要字段: name 或 count"}), 400
    
    try:
        # 获取IMEI字段（可选）
        imei = data.get('IMEI', None)
        # 获取项目类型字段（可选）
        device_type = data.get('device_type', None)
        
        # 保存数据到SQLite数据库
        with get_db_connection() as conn:
            cursor = conn.execute('INSERT INTO json_data (name, count, imei, device_type) VALUES (?, ?, ?, ?)',
                                  (data['name'], data['count'], imei, device_type))
            conn.commit()
            inserted_id = cursor.lastrowid
        
        # 返回处理结果
        return jsonify({
            "success": True,
            "id": inserted_id,
            "received_data": data,
            "message": "数据已成功接收和保存到数据库"
        }), 200
    except Exception as e:
        print(f"保存数据时出错: {e}")
        return jsonify({"error": f"保存数据时出错: {str(e)}"}), 500

# 添加新路由：获取下载目录下的所有子目录
@app.route('/api/subdirectories', methods=['POST'])
@app.route('/api/subdirectories/<device_name>/<path:subdir_path>', methods=['POST'])
@app.route('/api/subdirectories/<device_name>', methods=['POST'], defaults={'subdir_path': ''})
def get_subdirectories(device_name='SW610_HB02', subdir_path=''):
    """获取指定子目录下的所有文件下载地址和子目录列表"""
    try:
        # URL解码subdir_path，确保正确处理中文路径
        from urllib.parse import unquote
        subdir_path = unquote(subdir_path)
        device_name = unquote(device_name)
        
        # 从POST的JSON数据中获取设备名称（仅当URL路径中未提供设备名称时）
        data = request.get_json(silent=True)
        if data and 'device' in data and device_name == 'SW610_HB02':
            device_name = data['device']
        
        # 验证设备名称
        if device_name not in DEVICES:
            device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备SW610H01
        
        # 构建完整路径，包含设备目录
        full_path = os.path.join(DOWNLOAD_DIR, device_name, subdir_path)
        
        # 检查路径是否在下载目录内（防止路径遍历攻击）
        if not os.path.abspath(full_path).startswith(os.path.abspath(os.path.join(DOWNLOAD_DIR, device_name))):
            return jsonify({
                "success": False,
                "error": "无效路径"
            }), 400
        
        if not os.path.exists(full_path):
            return jsonify({
                "success": False,
                "error": "路径不存在"
            }), 404
            
        if not os.path.isdir(full_path):
            return jsonify({
                "success": False,
                "error": "路径不是目录"
            }), 400
        
        # 获取目录下的所有项目
        items = os.listdir(full_path)
        
        # 筛选出文件并生成下载链接
        files = []
        directories = []
        for item in items:
            item_path = os.path.join(full_path, item)
            if os.path.isfile(item_path):  # 处理文件
                # 为文件生成下载链接，URL编码文件名和路径部分
                from urllib.parse import quote
                encoded_item = quote(item)
                encoded_subdir_path = quote(subdir_path) if subdir_path else ''
                # 下载链接包含设备名称
                download_url = f"/downloads/{device_name}/{encoded_subdir_path}/{encoded_item}" if subdir_path else f"/downloads/{device_name}/{encoded_item}"
                files.append({
                    "name": item,
                    "url": download_url
                })
            elif os.path.isdir(item_path):  # 处理子目录
                # 为子目录生成访问链接，URL编码目录名和路径部分
                from urllib.parse import quote
                encoded_item = quote(item)
                encoded_subdir_path = quote(subdir_path) if subdir_path else ''
                # API链接包含设备名称
                dir_url = f"/api/subdirectories/{device_name}/{encoded_subdir_path}/{encoded_item}" if subdir_path else f"/api/subdirectories/{device_name}/{encoded_item}"
                directories.append({
                    "name": item,
                    "url": dir_url
                })
        
        # 按name字段对files和directories数组进行排序
        files.sort(key=lambda x: x['name'])
        directories.sort(key=lambda x: x['name'])
        
        # 返回文件列表和子目录列表
        return jsonify({
            "success": True,
            "device": device_name,  # 添加设备名称字段
            "files": files,
            "directories": directories,
            "subdir_count": len(directories),  # 添加子目录数量字段
            "files_count": len(files)  # 添加文件数量字段
        }), 200
    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        print(f"Error in get_subdirectories: {str(e)}\n{error_details}")
        return jsonify({
            "success": False,
            "error": f"获取文件列表时出错: {str(e)}"
        }), 500

@app.route('/upload-folder', methods=['POST'])
def upload_folder():
    """上传整个文件夹"""
    try:
        # 检查密码
        password = request.form.get('password')
        if not password:
            return jsonify({"success": False, "error": "缺少上传密码"}), 401
        
        # 验证密码 (实际应用中应该使用更安全的方式存储和验证密码)
        if password != "secure_password_123":  # 这里使用硬编码密码，实际项目中应该从配置文件或环境变量读取
            return jsonify({"success": False, "error": "上传密码错误"}), 401
        
        # 获取设备名称
        device_name = request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
        if device_name not in DEVICES:
            return jsonify({"success": False, "error": f"无效的设备名称: {device_name}"}), 400
        
        # 检查是否有文件上传
        if 'files' not in request.files:
            return jsonify({"success": False, "error": "没有文件部分"}), 400
        
        files = request.files.getlist('files')
        
        if not files or all(f.filename == '' for f in files):
            return jsonify({"success": False, "error": "没有选择文件"}), 400
        
        # 检查文件大小
        for file in files:
            if file.content_length and file.content_length > app.config['MAX_CONTENT_LENGTH']:
                return jsonify({"success": False, "error": "文件大小超过1MB限制"}), 400
        
        # 获取第一个文件的相对路径以确定文件夹名称
        first_file_path = files[0].filename
        if not first_file_path:
            return jsonify({"success": False, "error": "文件路径无效"}), 400
            
        folder_name = None
        if '\\' in first_file_path:  # Windows路径
            folder_name = first_file_path.split('\\')[0]
        elif '/' in first_file_path:  # Unix路径
            folder_name = first_file_path.split('/')[0]
        else:
            folder_name = 'uploaded_folder'
        
        if not folder_name:
            return jsonify({"success": False, "error": "无法确定文件夹名称"}), 400
        
        # 检查文件夹是否已存在，如果存在则拒绝上传
        device_download_dir = os.path.join(DOWNLOAD_DIR, device_name)
        if os.path.exists(os.path.join(device_download_dir, folder_name)):
            return jsonify({
                "success": False,
                "error": f"文件夹 '{folder_name}' 已存在，无法重复上传"
            }), 400
        
        # 创建目标文件夹
        target_folder = os.path.join(device_download_dir, folder_name)
        os.makedirs(target_folder, exist_ok=True)
        
        # 保存所有文件
        for file in files:
            if file and file.filename:
                # 获取文件的相对路径
                relative_path = file.filename
                if '\\' in relative_path:
                    relative_path_parts = relative_path.split('\\')[1:]  # 去除根文件夹名称
                elif '/' in relative_path:
                    relative_path_parts = relative_path.split('/')[1:]  # 去除根文件夹名称
                else:
                    relative_path_parts = [relative_path]
                
                # 构建目标路径
                target_path = os.path.join(target_folder, *relative_path_parts)
                
                # 确保目标目录存在
                target_dir = os.path.dirname(target_path)
                os.makedirs(target_dir, exist_ok=True)
                
                # 保存文件
                file.save(target_path)
        
        # 记录上传信息
        client_ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.environ.get('REMOTE_ADDR'))
        user_agent = request.headers.get('User-Agent')
        # 使用从请求头获取的设备名称或者默认值
        device_name = request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
        if device_name not in DEVICES:
            device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备SW610H01
        
        try:
            with get_db_connection() as conn:
                conn.execute('''INSERT INTO upload_records 
                               (folder_name, upload_type, client_ip, user_agent, device_name) 
                               VALUES (?, ?, ?, ?, ?)''', 
                            (folder_name, 'test_case', client_ip, user_agent, device_name))
                conn.commit()
        except Exception as e:
            print(f"记录上传信息失败: {e}")
        
        return jsonify({
            "success": True, 
            "folder_name": folder_name,
            "message": f"测试用例文件夹 '{folder_name}' 上传成功"
        }), 201
        
    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        print(f"上传文件夹时出错: {str(e)}\n{error_details}")
        return jsonify({"success": False, "error": f"上传失败: {str(e)}"}), 500

@app.route('/api/data/<int:record_id>', methods=['DELETE'])
def delete_data(record_id):
    """删除指定ID的数据记录"""
    try:
        with get_db_connection() as conn:
            cursor = conn.execute('DELETE FROM json_data WHERE id = ?', (record_id,))
            conn.commit()
            
            # 检查是否有记录被删除
            if cursor.rowcount == 0:
                return jsonify({
                    "success": False,
                    "error": "未找到指定ID的记录"
                }), 404
                
        return jsonify({
            "success": True,
            "message": f"ID为{record_id}的记录已成功删除"
        }), 200
    except Exception as e:
        return jsonify({
            "success": False,
            "error": f"删除数据时出错: {str(e)}"
        }), 500

@app.route('/excel')
def list_excel():
    """列出所有Excel文件和子目录"""
    # 获取设备名称，优先从查询参数获取，如果未指定则默认为SW610_HB02
    device_name = request.args.get('device') or request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
    if device_name not in DEVICES:
        device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备A
    
    def build_directory_tree(base_path, relative_path=''):
        items = os.listdir(os.path.join(base_path, relative_path))
        files = []
        directories = []
        
        for item in items:
            item_path = os.path.join(relative_path, item)
            full_path = os.path.join(base_path, item_path)
            if os.path.isdir(full_path):
                directories.append(item_path)
            else:
                files.append(item_path)
        
        # 对文件和目录名进行排序
        files.sort()
        directories.sort()
        
        return directories, files
    
    def generate_html_tree(base_path, current_path=''):
        directories, files = build_directory_tree(base_path, current_path)
        
        html_parts = []
        
        # 显示当前目录下的文件（已排序）
        for file in files:
            file_name = os.path.basename(file)
            # 添加下载整个目录的链接
            html_parts.append(f'<li><a href="/excel-download?device={device_name}&path={file}">{file_name}</a></li>')
        
        # 显示当前目录下的子目录（已排序）
        for directory in directories:
            dir_name = os.path.basename(directory)
            dir_url = f"/excel?device={device_name}&path={directory}"
            # 添加下载整个目录的链接
            download_url = f"/excel-download?device={device_name}&path={directory}"
            html_parts.append(f'<li><strong>{dir_name}/</strong> [<a href="{download_url}">下载整个目录</a>]')
            # 递归显示子目录内容
            sub_html = generate_html_tree(base_path, directory)
            if sub_html:
                html_parts.append(f'<ul>{sub_html}</ul>')
            html_parts.append('</li>')
        
        return ''.join(html_parts)
    
    # 获取请求中的路径参数
    path = request.args.get('path', '')
    
    # URL解码路径参数，确保正确处理中文和特殊字符
    from urllib.parse import unquote
    decoded_path = unquote(path)
    
    # 构建完整路径，包含设备目录
    full_path = os.path.join(EXCEL_DIR, device_name, decoded_path)
    
    # 检查路径是否在Excel目录内（防止路径遍历攻击）
    if not os.path.abspath(full_path).startswith(os.path.abspath(os.path.join(EXCEL_DIR, device_name))):
        return "无效路径", 400
    
    if not os.path.exists(full_path):
        return "路径不存在", 404
    
    if not os.path.isdir(full_path):
        return "路径不是目录", 400
    
    # 生成目录树HTML
    file_tree_html = generate_html_tree(os.path.join(EXCEL_DIR, device_name), decoded_path)
    
    if not file_tree_html:
        return "目录为空"
    
    return f"""
    <h1>Excel测试用例文件 (设备: {device_name})</h1>
    <p>当前路径: /{decoded_path}</p>
    <ul>
        {file_tree_html}
    </ul>
    <p><a href="/">返回主页</a></p>
    """

@app.route('/excel-download')
def download_excel_folder():
    """打包并下载整个Excel文件夹"""
    try:
        # 获取设备名称，如果未指定则默认为SW610H01
        device_name = request.args.get('device') or request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
        if device_name not in DEVICES:
            device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备SW610H01
        
        path = request.args.get('path', '')
        if not path:
            return "缺少path参数", 400
        
        # URL解码路径参数
        from urllib.parse import unquote
        decoded_path = unquote(path)
        
        # 构建完整路径，包含设备目录
        full_path = os.path.join(EXCEL_DIR, device_name, decoded_path)
        
        # 检查路径是否在Excel目录内（防止路径遍历攻击）
        if not os.path.abspath(full_path).startswith(os.path.abspath(os.path.join(EXCEL_DIR, device_name))):
            return "无效路径", 400
        
        if not os.path.exists(full_path):
            return "路径不存在", 404
        
        folder_name = os.path.basename(decoded_path)
        
        if os.path.isfile(full_path):
            # 如果是文件，直接返回文件
            directory = os.path.dirname(full_path)
            file_name = os.path.basename(full_path)
            return send_from_directory(directory, file_name, as_attachment=True, download_name=file_name)
        elif os.path.isdir(full_path):
            # 如果是目录，打包成zip文件后返回
            # 创建内存中的zip文件
            memory_file = BytesIO()
            with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for root, dirs, files in os.walk(full_path):
                    for file in files:
                        file_path = os.path.join(root, file)
                        # 计算在zip文件中的相对路径
                        arc_path = os.path.join(folder_name, os.path.relpath(file_path, full_path))
                        zipf.write(file_path, arc_path)
            
            # 重置内存文件指针
            memory_file.seek(0)
            
            # 发送zip文件
            return send_file(
                memory_file,
                mimetype='application/zip',
                as_attachment=True,
                download_name=f'{folder_name}.zip'
            )
        else:
            return "路径既不是文件也不是目录", 400
    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        print(f"下载Excel文件夹时出错: {str(e)}\n{error_details}")
        return f"下载文件夹时出错: {str(e)}", 500

@app.route('/upload-excel-folder', methods=['POST'])
def upload_excel_folder():
    """上传整个Excel文件夹"""
    try:
        # 获取设备名称
        device_name = request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
        if device_name not in DEVICES:
            return jsonify({"success": False, "error": f"无效的设备名称: {device_name}"})
        
        # 检查是否有文件上传
        if 'files' not in request.files:
            return jsonify({"success": False, "error": "没有文件部分"}), 400
        
        files = request.files.getlist('files')
        
        if not files or all(f.filename == '' for f in files):
            return jsonify({"success": False, "error": "没有选择文件"}), 400
        
        # 检查文件数量，每个目录限制在15个以下
        if len(files) > 15:
            return jsonify({"success": False, "error": "单个目录中的文件数量不能超过15个"}), 400
        
        # 检查文件大小
        for file in files:
            if file.content_length and file.content_length > app.config['MAX_CONTENT_LENGTH']:
                return jsonify({"success": False, "error": "文件大小超过1MB限制"}), 400
        
        # 获取第一个文件的相对路径以确定文件夹名称
        first_file_path = files[0].filename
        if not first_file_path:
            return jsonify({"success": False, "error": "文件路径无效"}), 400
            
        folder_name = None
        if '\\' in first_file_path:  # Windows路径
            folder_name = first_file_path.split('\\')[0]
        elif '/' in first_file_path:  # Unix路径
            folder_name = first_file_path.split('/')[0]
        else:
            folder_name = 'excel_report'
        
        if not folder_name:
            return jsonify({"success": False, "error": "无法确定文件夹名称"}), 400
        
        # 检查文件夹是否已存在，如果存在则拒绝上传
        device_excel_dir = os.path.join(EXCEL_DIR, device_name)
        if os.path.exists(os.path.join(device_excel_dir, folder_name)):
            return jsonify({
                "success": False,
                "error": f"文件夹 '{folder_name}' 已存在，无法重复上传"
            }), 400
        
        # 收集Excel文件列表
        excel_files = []
        for file in files:
            if file and file.filename:
                # 获取文件的相对路径
                relative_path = file.filename
                if '\\' in relative_path:
                    relative_path_parts = relative_path.split('\\')[1:]  # 去除根文件夹名称
                elif '/' in relative_path:
                    relative_path_parts = relative_path.split('/')[1:]  # 去除根文件夹名称
                else:
                    relative_path_parts = [relative_path]
                
                # 检查是否为xlsx文件
                if relative_path_parts and relative_path_parts[-1].endswith('.xlsx'):
                    excel_files.append(relative_path_parts[-1])
        
        # 检查Excel文件名规则
        if excel_files:
            # 提取文件名前缀数字
            prefixes = []
            for filename in excel_files:
                # 检查文件名是否符合规则: 以数字开头，后跟下划线和描述
                import re
                match = re.match(r'^(\d+)_', filename)
                if not match:
                    return jsonify({
                        "success": False, 
                        "error": f"文件名 '{filename}' 不符合命名规则。文件名必须以数字开头，后跟下划线和描述，例如: '10_打开设置.xlsx'"
                    }), 400
                
                prefix = int(match.group(1))
                # 检查数字是否大于等于10
                if prefix < 10:
                    return jsonify({
                        "success": False, 
                        "error": f"文件名 '{filename}' 不符合命名规则。文件名必须以10或更大的数字开头，例如: '10_打开设置.xlsx'"
                    }), 400
                    
                prefixes.append(prefix)
            
            # 检查数字是否按升序排列（允许有间隔）
            if prefixes:
                sorted_prefixes = sorted(prefixes)
                # 检查是否所有数字都大于等于10
                for prefix in sorted_prefixes:
                    if prefix < 10:
                        return jsonify({
                            "success": False, 
                            "error": f"文件名不符合命名规则。所有文件名必须以10或更大的数字开头"
                        }), 400
        
        # 创建目标文件夹
        target_folder = os.path.join(device_excel_dir, folder_name)
        os.makedirs(target_folder, exist_ok=True)
        
        # 保存所有文件
        saved_files = []
        for file in files:
            if file and file.filename:
                # 获取文件的相对路径
                relative_path = file.filename
                if '\\' in relative_path:
                    relative_path_parts = relative_path.split('\\')[1:]  # 去除根文件夹名称
                elif '/' in relative_path:
                    relative_path_parts = relative_path.split('/')[1:]  # 去除根文件夹名称
                else:
                    relative_path_parts = [relative_path]
                
                # 构建目标路径
                target_path = os.path.join(target_folder, *relative_path_parts)
                
                # 确保目标目录存在
                target_dir = os.path.dirname(target_path)
                os.makedirs(target_dir, exist_ok=True)
                
                # 保存文件
                file.save(target_path)
                saved_files.append({
                    'path': target_path,
                    'relative_path': os.path.join(*relative_path_parts) if relative_path_parts else ''
                })
        
        # 处理Excel文件转换为bin文件
        for saved_file in saved_files:
            file_path = saved_file['path']
            relative_path = saved_file['relative_path']
            
            # 检查是否为xlsx文件且文件名以.xlsx结尾
            if relative_path.endswith('.xlsx'):
                try:
                    # 读取Excel文件
                    df = pd.read_excel(file_path)
                    
                    # 尝试匹配列名，处理可能的空格或大小写问题
                    column_mapping = {}
                    for col in df.columns:
                        clean_col = col.strip().lower()
                        if clean_col in ['步骤', 'step', 'id']:
                            column_mapping['步骤'] = col
                        elif clean_col in ['设备', 'device', 'type']:
                            column_mapping['设备'] = col
                        elif clean_col in ['动作', 'action', '动作']:
                            column_mapping['动作'] = col
                        elif clean_col in ['x1', 'x']:
                            column_mapping['X1'] = col
                        elif clean_col in ['y1', 'y']:
                            column_mapping['Y1'] = col
                        elif clean_col in ['x2']:
                            column_mapping['X2'] = col
                        elif clean_col in ['y2']:
                            column_mapping['Y2'] = col
                        elif clean_col in ['持续时间', 'duration', 'ktime']:
                            column_mapping['持续时间'] = col
                        elif clean_col in ['执行完延时', 'delay', 'dtime']:
                            column_mapping['执行完延时'] = col
                        elif clean_col in ['界面切换', 'check']:
                            column_mapping['界面切换'] = col
                    
                    # 转换数据为JSON数组
                    json_data = []
                    for index, row in df.iterrows():
                        # 获取步骤值并检查是否为自然数
                        step_value = row.get(column_mapping.get('步骤'))
                        if pd.notna(step_value):
                            # 检查步骤值是否为自然数
                            try:
                                step_int = int(step_value)
                                if step_int < 0:  # 不是自然数
                                    continue  # 跳过这一行
                            except (ValueError, TypeError):
                                # 不能转换为整数的值，跳过这一行
                                continue
                        else:
                            # 空值也跳过
                            continue
                        
                        # 创建JSON对象，按照指定格式，使用更安全的获取方式
                        try:
                            json_obj = {
                                "id": int(row.get(column_mapping.get('步骤'), 0)) if pd.notna(row.get(column_mapping.get('步骤'))) else 0,
                                "type": int(row.get(column_mapping.get('设备'), 0)) if pd.notna(row.get(column_mapping.get('设备'))) else 0,
                                "action": int(row.get(column_mapping.get('动作'), 0)) if pd.notna(row.get(column_mapping.get('动作'))) else 0,
                                "x1": int(row.get(column_mapping.get('X1'), 0)) if pd.notna(row.get(column_mapping.get('X1'))) else 0,
                                "y1": int(row.get(column_mapping.get('Y1'), 0)) if pd.notna(row.get(column_mapping.get('Y1'))) else 0,
                                "x2": int(row.get(column_mapping.get('X2'), 0)) if pd.notna(row.get(column_mapping.get('X2'))) else 0,
                                "y2": int(row.get(column_mapping.get('Y2'), 0)) if pd.notna(row.get(column_mapping.get('Y2'))) else 0,
                                "ktime": int(row.get(column_mapping.get('持续时间'), 0)) if pd.notna(row.get(column_mapping.get('持续时间'))) else 0,
                                "dtime": int(row.get(column_mapping.get('执行完延时'), 0)) if pd.notna(row.get(column_mapping.get('执行完延时'))) else 0,
                                "check": int(row.get(column_mapping.get('界面切换'), 0)) if pd.notna(row.get(column_mapping.get('界面切换'))) else 0
                            }
                            json_data.append(json_obj)
                        except (ValueError, TypeError):
                            # 如果任何字段无法转换为整数，则跳过这一行
                            continue
                        
                    # 创建对应的bin文件路径（也在设备目录下）
                    bin_relative_path = relative_path.replace('.xlsx', '.bin')
                    bin_full_path = os.path.join(DOWNLOAD_DIR, device_name, folder_name, bin_relative_path)
                    
                    # 确保bin文件的目录存在
                    bin_dir = os.path.dirname(bin_full_path)
                    os.makedirs(bin_dir, exist_ok=True)
                    
                    # 将JSON数据写入bin文件
                    with open(bin_full_path, 'w', encoding='utf-8') as f:
                        # 将数组包装在"data"键下
                        wrapped_data = {"data": json_data}
                        json.dump(wrapped_data, f, ensure_ascii=False, indent=2)
                        
                except Exception as e:
                    print(f"处理Excel文件 {file_path} 时出错: {str(e)}")
                    import traceback
                    traceback.print_exc()
                    # 继续处理其他文件，不中断整个过程
        
        # 记录上传信息
        client_ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.environ.get('REMOTE_ADDR'))
        user_agent = request.headers.get('User-Agent')
        # 使用从请求头获取的设备名称或者默认值
        device_name = request.headers.get('X-Device-Name') or request.headers.get('Device-Name') or "SW610_HB02"
        if device_name not in DEVICES:
            device_name = "SW610_HB02"  # 如果设备名称无效，则使用默认设备SW610H01
        
        try:
            with get_db_connection() as conn:
                conn.execute('''INSERT INTO upload_records 
                               (folder_name, upload_type, client_ip, user_agent, device_name) 
                               VALUES (?, ?, ?, ?, ?)''', 
                            (folder_name, 'excel', client_ip, user_agent, device_name))
                conn.commit()
        except Exception as e:
            print(f"记录上传信息失败: {e}")
        
        return jsonify({
            "success": True, 
            "folder_name": folder_name,
            "message": f"Excel测试用例文件夹 '{folder_name}' 上传成功"
        }), 201
        
    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        print(f"上传Excel文件夹时出错: {str(e)}\n{error_details}")
        return jsonify({"success": False, "error": f"上传失败: {str(e)}"}), 500

if __name__ == '__main__':
    # 初始化数据库
    init_db()
    app.run(host='0.0.0.0', port=80, debug=True)